package com.improvess.rna.som;

public class KohonenSOM extends RedeSOM {

	private double talVizinhanca;
	private double talAprendizagem;
	private double niZero;
	private DataSet dataSetInicializacaoPesos;
	private double[][] registros;

	public KohonenSOM(int quantidadeDeNeuronios, int quantidadeDeAtributos,
			double sigmaVizinhanca, double talVizinhanca,
			double talAprendizagem, double niZero,
			DataSet dataSetInicializacaoPesos) {
		super(quantidadeDeNeuronios, quantidadeDeAtributos, sigmaVizinhanca);
		this.talVizinhanca = talVizinhanca;
		this.talAprendizagem = talAprendizagem;
		this.niZero = niZero;
		this.dataSetInicializacaoPesos = dataSetInicializacaoPesos;
		inicializarNeuronios();
	}

	private double atualizarPesos(double[] x, int tempo) {

		Coordenada vencedor = vencedor(x).getCoordenada();
		double retorno = 0;
		for (int i = 0; i < this.getNeuronios().length; i++) {
			for (int k = 0; k < this.getNeuronios().length; k++) {
				NeuronioKohonen neuronioKohonen = (NeuronioKohonen) this
						.getNeuronios()[i][k];
				retorno = retorno
						+ neuronioKohonen.atualizarPesos(x, vencedor, tempo);
			}
		}
		return retorno;
	}

	public int aprendizagem(double[][] treinamento) {

		int tempo = 0;
		final double T = 0.0001;
		double erroAcumulado;
		double ultimoErroAcumulado;
		boolean teste;
		this.registros = treinamento;
		do {
			erroAcumulado = 0;
			ultimoErroAcumulado = Double.MAX_VALUE;
			for (int i = 0; i < this.registros.length; i++) {
				erroAcumulado = erroAcumulado
						+ atualizarPesos(this.registros[i], tempo);
				tempo++;
			}

			teste = erroAcumulado > T && ultimoErroAcumulado > erroAcumulado;
			ultimoErroAcumulado = erroAcumulado;
		} while (teste);
		return tempo;
	}

	public double getTalAprendizagem() {
		return talAprendizagem;
	}

	public double getTalVizinhanca() {
		return talVizinhanca;
	}

	public double getNiZero() {
		return niZero;
	}

	@Override
	protected NeuronioKohonen inicializarNeuronio(int i, int k) {
		return new NeuronioKohonen(this.getQuantidadeDeAtributos(), i, k, this
				.getSigmaVizinhanca(), this.talVizinhanca,
				this.talAprendizagem, this.niZero,
				this.dataSetInicializacaoPesos);
	}

	@Override
	public Neuronio vencedor(int i) {
		double[] x = this.registros[i];
		return this.vencedor(x);
	}

	private Neuronio vencedor(double[] x) {

		double menor = Double.MAX_VALUE;
		Neuronio retorno = this.getNeuronios()[0][0];
		for (int i = 0; i < this.getNeuronios().length; i++) {
			for (int k = 0; k < this.getNeuronios().length; k++) {
				NeuronioKohonen neuronio = (NeuronioKohonen) this
						.getNeuronios()[i][k];
				double[] pesos = neuronio.getPesos();
				double arg = 0;
				for (int j = 0; j < x.length; j++) {
					double temp = x[j] - pesos[j];
					arg = arg + temp * temp;
				}
				arg = Math.sqrt(arg);
				if (arg < menor) {
					menor = arg;
					retorno = neuronio;
				}
			}
		}
		return retorno;
	}

}
